////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_SCRIPTING_EXPRESSION_H__
#define __WN_SCRIPTING_EXPRESSION_H__

#include "WNScripting/inc/WNParseNode.h"

namespace WNScripting {
    enum WNExpressionType {
        eWNIdentifierExpression,
        eWNConstantExpression,
        eWNArithmeticAdd,
        eWNArithmeticSubtract,
        eWNArithmeticMultiply,
        eWNArithmeticMod,
        eWNArithmeticDivide,
        eWNArithmeticEquality,
        eWNReferenceTypeInitialization,
        eWNConstantStringInitialization,
        eWNArithmeticSquareBrackets,
        eWNFunctionCall,
        eWNStructAccess,
        eWNReferenceCreation,
        eWNArrayLength
    };

    enum WNExpressionConstantInt {
        eWNConstantIntExpression
    };

    enum WNExpressionConstantFloat {
        eWNConstantFloatExpression
    };

    enum WNExpressionConstantChar {
        eWNConstantCharExpression
    };

    enum WNExpressionConstantType {
        eWNConstantInt,
        eWNConstantFloat,
        eWNConstantChar
    };

    class WNDataType;
    class WNExpressionList;

    class WNExpression : public WNParseNode {
    public:
        virtual ~WNExpression();
        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNIdentifierElement _identifier);

        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNS_INT_CONST _constVal,
                     WNExpressionConstantInt _constInt);
    
        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNS_CHAR_CONST _constVal,
                     WNExpressionConstantChar _constChar);

        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNS_FLOAT_CONST _constVal,
                     WNExpressionConstantFloat);

        //TODO: Merge ExpressionType with WNStaticArithmetic
        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNExpressionType _type,
                     WNExpression* _LHS,
                     WNExpression* _RHS);

        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNExpression* _LHS,
                     WNExpression* _RHS,
                     WNStaticPredicate _pred);

        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNDataType* _dataType,
                     WN_BOOL _heapAlloc);
    
        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNIdentifierElement _identifier,
                     WNExpressionList* _params);

        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNExpression* _LHS,
                     WNIdentifierElement _structIdentifier);

        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNDataType* _dataType,
                     WN_BOOL _heap,
                     std::vector<WNExpression*>* _exprList,
                     WN_BOOL _ignoreLastCreation);

        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNS_STRING_CONST _dataType,
                     WN_BOOL _heapAlloc);

        WNExpression(WN_SIZE_T _startLine,
                     WN_SIZE_T _endLine,
                     WN_SIZE_T _startChar,
                     WN_SIZE_T _endChar,
                     WNExpression* _expr,
                     WNExpressionType _type);

        WNTypeElement GetType();
        WN_BOOL ConstantFold(WNScriptingEngineData& _scriptingData);
        virtual llvm::Value* OptimizeAndGenerate(WNScriptingEngineData& _scriptingData, WNFunctionDecl* _functionDecl);
        virtual WN_BOOL ReportErrors();

    private:
        //TODO make constVal not a pointer (we want to be able to specify floating point size)
        union {
            WNIdentifierElement mIdentifier;
            WNExpression* mLHS;
            WNDataType* mDataInitializer;
            WNS_STRING_CONST mStringInitializer;
            WN_CHAR mConstVal[WN_SCRIPTING_MAX_CONST_SIZE];
        } mFirstElement;

        union {
            WNExpression* mRHS;
            WNExpressionList* mExpressionList;
            WNIdentifierElement mIdentifier;
            std::vector<WNExpression*>* mArrayExpressions;
        } mSecondElement;

        WN_BOOL mHeapAlloc;
        WN_BOOL mFolded;
        WNExpressionConstantType mConstType;
        WNExpressionType mType;
        WNTypeElement mElementType;
        WNStaticPredicate mPredicate;
        WN_BOOL mSuppressLastCreation;
    };
}
#endif // __WN_SCRIPTING_EXPRESSION_H__